import hashlib
import logging
import base64
import socket
import struct
from utils import create_nonce_str
from Crypto.Cipher import AES


_logger = logging.getLogger("WX_MSG_PROC")


def signature_url(token, timestamp, nonce, encrypt):
    """用SHA1算法生成安全签名
    @:param token:  票据
    @:param timestamp: 时间戳
    @:param encrypt: 密文
    @:param nonce: 随机字符串
    @:return: 安全签名
    """
    try:
        sign_str = f"jsapi_ticket={token.decode()}&noncestr={nonce}&timestamp={timestamp}&url={encrypt}"
        _logger.debug(f"URL签名串内容为：{sign_str}")
        sha = hashlib.sha1()
        sha.update(sign_str.encode("ascii"))
        return sha.hexdigest()
    except Exception as e:
        _logger.error(e)
        return None


def verify_signature_public(signature, timestamp, nonce, token) -> bool:
    """用SHA1算法校验安全签名，用于公众号和服务号的验证
    @:param signature: 目标签名
    @:param timestamp: 时间戳
    @:param nonce: 随机字符串
    @:param token: 票据
    @:return: 是否校验通过
    """
    try:
        sort_list = [token, timestamp, nonce]
        sort_list.sort()
        sha = hashlib.sha1()
        sha.update("".join(sort_list).encode("ascii"))
        return sha.hexdigest() == signature
    except Exception as e:
        _logger.error(e)
        return False


def verify_signature_enterprise(signature, timestamp, nonce, echo, token) -> bool:
    """用SHA1算法校验安全签名，用于企业微信应用的验证
    @:param signature: 目标签名
    @:param timestamp: 时间戳
    @:param nonce: 随机字符串
    @:param echo: 给定密文
    @:param token: 票据
    @:return: 是否校验通过
    """
    try:
        sort_list = [token, timestamp, nonce, echo]
        sort_list.sort()
        sha = hashlib.sha1()
        sha.update("".join(sort_list).encode("utf-8"))
        return sha.hexdigest() == signature
    except Exception as e:
        _logger.error(e)
        return False


def decrypt_message(encrypt, cipher, remove_pad=False):
    """解密微信发来的消息
    @:param encrypt: 已加密的密文
    @:return: 解密后的信息
    """
    try:
        key = base64.b64decode(cipher + "=")
        cryptor = AES.new(key, AES.MODE_CBC, key[:16])
        plain_text = cryptor.decrypt(base64.b64decode(encrypt))
        if remove_pad:
            plain_text = plain_text[16:]
        else:
            pad = plain_text[-1]
            plain_text = plain_text[16:-pad]
        content_len = socket.ntohl(struct.unpack("I", plain_text[:4])[0])
        content = plain_text[4:content_len+4]
        return content
    except Exception as e:
        _logger.error(f"[DECRYPT_MESSAGE] {e}")
        return None


def encrypt_message(text, cipher, receiveid):
    """对明文进行加密
    @:param text: 需要加密的明文
    @:param cipher: 密钥
    @:param receiveid: 附加特征串
    @:return: 加密得到的字符串
    """
    key = base64.b64decode(cipher + "=")
    # 16位随机字符串添加到明文开头
    text = create_nonce_str(16) + struct.pack("I", socket.htonl(len(text))) + text + receiveid
    # 使用自定义的填充方式对明文进行补位填充
    text = pkcs7_encode(text)
    # 加密
    cryptor = AES.new(key, AES.MODE_CBC, key[:16])
    try:
        encrypted_text = cryptor.encrypt(text)
        # 使用BASE64对加密后的字符串进行编码
        return base64.b64encode(encrypted_text)
    except Exception as e:
        _logger.error(f"[ENCRYPT_MESSAGE] {e}")
        return None


def pkcs7_encode(text):
    """对需要加密的明文进行填充补位
    @:param text: 需要进行填充补位操作的明文
    @:return: 补齐明文字符串
    """
    block_size = 32
    text_length = len(text)
    # 计算需要填充的位数
    amount_to_pad = block_size - (text_length % block_size)
    if amount_to_pad == 0:
        amount_to_pad = block_size
    # 获得补位所用的字符
    pad = chr(amount_to_pad)
    return text + pad * amount_to_pad


def pkcs7_decode(decrypted):
    """删除解密后明文的补位字符
    @:param decrypted: 解密后的明文
    @:return: 删除补位字符后的明文
    """
    pad = ord(decrypted[-1])
    if pad < 1 or pad > 32:
        pad = 0
    return decrypted[:-pad]
